/*
 * Na šachovnici P (obdelnikovou sit m * k) je pevně rozmístěno f bodů.
 * Úkolem je rozmístit dalších b-f bodů, tak aby byl maximální součet matice
 * Euklidovských vzdáleností všech dvojic bodů = sum_i_j d(i,j). Body se nesmějí
 * překrývat.
 *
 * Geometrická interpretace: rozmístit na ploše b bodů tak, aby byly co nejdál
 * od sebe.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "main.h"
#include "io.h"
#include "state.h"
#include "stack.h"
#include "plane.h"
#include "util.h"
#include "stateCuda.h"

#define INPUT_FILE_PATH "input.txt"

/* unsigned long long int je zoufale málo pro tyto potřeby */

/* unsigned long long int combinations;
 unsigned long long int progress = 0;
 int percents = 0; */

void doWork(void);
void processState(State *state);

/* Nejlepší dosavadní řešení */
State *bestState;
float bestValue = -1;
/*
 * Hlavní funkce programu.
 */
int main() {
	FILE *inputFile;
	int i;
	clock_t start, end;
	/* nulové buffery pro výstup --> pro ladění při segfaultech */
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);

	printf("Vítejte v programu nenávist!\n");

	/* otevřeme soubor se vstupními daty */
	inputFile = fopen(INPUT_FILE_PATH, "r");
	if(inputFile == NULL){
		fprintf(stderr, "ERROR READING INPUT FILE %s\n", INPUT_FILE_PATH);
		return EXIT_FAILURE;
	}

	/* načteme vstupní data */
	loadInput(inputFile);

	/* validace vstupních dat */
	if(b > m * k){
		fprintf(stderr, "Je specifikováno více bodů, než se na plán může vejít (%d > %d*%d)\n", b, m, k);
		return EXIT_FAILURE;
	}
	if(b < f){
		fprintf(
				stderr,
				"Počet pevně umístěných bodů je větší než celkový počet bodů: (b < f, %d < %d). Nelze umístit záporný počet bodů.\n",
				b, f);
		return EXIT_FAILURE;
	}
	for(i = 0; i < f; i++){
		if(fixedPoints[i].x >= m || fixedPoints[i].y >= k){
			fprintf(stderr, "Jedna souřadnice je specifikována mimo plán ([%d, %d])\n", fixedPoints[i].x,
					fixedPoints[i].y);
			return EXIT_FAILURE;
		}
	}

	/* int N = m*k - f, K = b - f;
	 combinations = fact(N)/(fact(K)*fact(N - K)); */

	/* výpis vstupních dat */
	printf("Vstupní data: m = %d, k = %d, f = %d, b = %d.\n", m, k, f, b);
	for(i = 0; i < f; i++){
		printf("%d: [%d,%d] ", i, fixedPoints[i].x, fixedPoints[i].y);
	}
	printf("\n");
	/* printf("Počet kombinací: %d", combinations); */

	/* Zadání počátečního stavu */
	stackPush(allocateState(0));

	start = clock();
	/* Spuštění práce */
	doWork();

	end = clock();

	printf("Celkový čas: %f s", ((float) (end - start)) / CLOCKS_PER_SEC);

	return 0;
}

/*
 * Pracovní metoda programu s hlavním cyklem, který má na zodpovědnost zpracovat
 * všechny stavy. Po skončení vypíše nejlepší řešení.
 */
void doWork(void) {
	State *state;

	while ((state = stackPop()) != NULL){
		processState(state);
	}

	printf("BEST SOLUTION:\n");
	printPlane(bestState);
	printState(bestState);
	printf("Solution value: %f\n", bestValue);
}

/*
 * Metoda zpracuje stav - buď spočítá jeho hodnotu (pro konečné stavy) nebo
 * vygeneruje potomky.
 */
void processState(State *state) {
	/*printf("--------------------\n");
	 printPlane(state);*/
	 printState(state);

	/* Pro stavy na konečné hloubce - listy */
	if(state->depth == b - f){
		float result = state->value;
		/* printf("Result is: %f\n", result); */
		if(result > bestValue){
			bestValue = result;
			bestState = state;
		}
		else{
			freeState(state);
		}
		/*progress++;
		 if(100 * progress / combinations > percents){
		 percents = 100 * progress / combinations;
		 //printf("%d%%\n", percents);
		 }*/
	}
	/* Pro stavy ve stromu - větve */
	else{
		generateChildren(state);
		freeState(state);
	}
}


